using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Windows.Forms.Design;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Compiler;
using Amazon.EC2;
using Amazon.EC2.Model;
using Citrix.WorkflowStudio.Common;
using Citrix.WorkflowStudio.CustomActivities.Designers;
using Citrix.WorkflowStudio.CustomActivities.Editors;
using Citrix.WorkflowStudio.CustomActivities.Serializers;
using Citrix.WorkflowStudio.User;

namespace Citrix.WorkflowStudio.Activities.AmazonEC2ActivityLibrary
{
    #region Attribute Definitions and Comments
    // [Designer(typeof(BaseActivityDesigner))]
    //     Defines what activity designer will be used for the current activity.
    //     The "BaseActivityDesigner" is the standard Citrix designer.
    // [DisplayNameAttribute(@"MyActivity")]
    //     Allows you to specify the name for your activity.
    //     This is the name that will appear in the toolbox, and the same name that
    //     will appear on the activity in the design surface.
    // [Description(@"This activity <...>.")]
    //     Allows you to provide a brief text description of the activity.
    //     This text will appear at the bottom of the toolbox in the Designer console
    //     when the activity is selected in the tree view and in the tooltip when
    //     the user hovers over the activity on the design surface. Typically, this 
    //     text is short and simply gives a high-level overiew of what the task does.
    // [ActivityTreePath(@"Folder/SubFolder")]
    //     Allows you to specify the name to use for this activity in the toolbox.
    //     NOTE: Use forward slashes not backward slashes to denote nested folders. 
    // [ActivityValidator(typeof(MyActivityValidator))]
    //     Defines which class is responsible for the validation logic of the activity.
    //     Typically each activity has its own dedicated validator class.
    // [BaseActivityDesigner(ImageResourceName = @"Resources.$safeclassname$32.png", AssemblyType = typeof($safeclassname$))]
    //     Allows you to specify any one, or multiple, of the following attributes
    //     to modify the look of activities when placed on the design surface
    //         ImageResourceName        GlowIntensity        TextForeColor         TextFontSize
    //         ShineColor               BackColor            ShapeRadius           Width
    //         GlowColor                MaskColor            TextFontFamily        Height
    //     Typically the ImageResourceName is used to provide an image to replace the default.
    //     The AssemblyType is required and must be set to the type of the Activity class.
    // [ToolboxBitmapAttribute(typeof($safeclassname$), @"Resources.$safeclassname$16.png")]
    //     Allows you to specify the 16x16 image file to use as the icon for the activity in the toolbox.
    // [DesignerSerializer(typeof(BindingListSerializer), typeof(WorkflowMarkupSerializer))]
    //     If you have any BindingList<> properties in this activity your activity needs to declare
    //     a DesignerSerializer attribute calling the Citrix BindingListSerializer.
    // [ActivityReferenceAssemblies(new string[] { "$snapinname$, Version=1.0.0.0, Culture=neutral, PublicKeyToken=12345ABCD6789EFG" } )]
    //     Allows you to specify one or more additional assemblies that assemblies that you want
    //     referenced by a project when the activity is dropped on the design surface.
    //     Rreferences that are added to the activity project should be added here as well.
    [Designer(typeof(BaseActivityDesigner))]
    [DisplayNameAttribute(@"Get Instances")]
    [Description(@"Retrieves all the instances currently running on Amazon EC2.")]
    [ActivityTreePath(@"Amazon/EC2")]
    [ActivityValidator(typeof(GetInstancesValidator))]
    [ToolboxBitmapAttribute(typeof(GetInstances), @"Resources.GetInstance_16.png")]
    [BaseActivityDesigner(ImageResourceName = @"Resources.GetInstance_32.png", AssemblyType = typeof(GetInstances))]
    [ActivityReferenceAssemblies(new string[] { "Amazon.EC2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c922ecace76be110" })]
    #endregion Attribute Definitions and Comments

    public partial class GetInstances : ActivityBase
    {
        #region Constructor
        public GetInstances()
        {
            InitializeComponent();
        }
        #endregion Constructor

        // Dependency Property definition section
        //     Dependency properties are properties that will be bound to other properties within the workflow.
        #region Dependency Properties

        #region AccessKey Property
        public static DependencyProperty AccessKeyProperty = DependencyProperty.Register("AccessKey", typeof(String), typeof(GetInstances));
        [DefaultValue("")]
        [Category(@"Parameters")]
        [DisplayName(@"Access Key ID")]
        [Description(@"Specify your Amazon EC2 Access Key ID. This value can be located by clicking on 'Your Account' from the AWS Management Console.")]
        [Browsable(true)]
        [EditorAttribute(typeof(TextEditor), typeof(UITypeEditor))]
        public String AccessKey
        {
            get
            {
                return ((String)(base.GetValue(AccessKeyProperty)));
            }
            set
            {
                base.SetValue(AccessKeyProperty, value);
            }
        }
        #endregion AccessKey Property

        #region SecretKey Property
        public static DependencyProperty SecretKeyProperty = DependencyProperty.Register("SecretKey", typeof(String), typeof(GetInstances));
        [Category(@"Parameters")]
        [DisplayName(@"Secret Access Key")]
        [Description(@"Specify your Amazon EC2 Secret Access Key. This value can be located by clicking on 'Your Account' from the AWS Management Console.")]
        [Browsable(true)]
        [EditorAttribute(typeof(PasswordEditor), typeof(UITypeEditor))]
        [BindTypes(new Type[] { typeof(String) })]
        public object SecretKey
        {
            get
            {
                if (HiddenSecretKey != null && HiddenSecretKey.encryptionType != CTXPasswordEncryptionType.NotSet)
                    return HiddenSecretKey.ToString();
                else
                    return base.GetValue(GetInstances.SecretKeyProperty);
            }
            set
            {
                if (value != null)
                {
                    EncryptedPasswordInfo info = new EncryptedPasswordInfo();
                    if (info.SetData(value.ToString()))
                    {
                        HiddenSecretKey = info;
                    }
                }
            }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public EncryptedPasswordInfo HiddenSecretKey
        {
            get
            {
                EncryptedPasswordInfo inf = new EncryptedPasswordInfo();
                string val = (string)base.GetValue(GetInstances.SecretKeyProperty);
                if (val != null)
                    inf.SetData(val);
                return inf;
            }
            set
            {
                if (value != null)
                {
                    base.SetValue(GetInstances.SecretKeyProperty, value.GetString());
                }
                else
                    base.SetValue(GetInstances.SecretKeyProperty, null);
            }
        }
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public string SecretKeyData
        {
            get
            {
                if (HiddenSecretKey != null)
                    return HiddenSecretKey.GetString();
                else
                    return null;
            }
            set
            {

                EncryptedPasswordInfo info = new EncryptedPasswordInfo();
                if (info.SetData(value) && info.encryptionType != CTXPasswordEncryptionType.NotSet)
                {
                    HiddenSecretKey = info;
                }
            }
        }
        #endregion SecretKey Property

        #region Output Property
        public static DependencyProperty OutputProperty = DependencyProperty.Register("Output", typeof(List<EC2InstanceDescription>), typeof(GetInstances));
        [Category(@"Read-Only Output")]
        [DisplayName(@"Output")]
        [Description(@"Contains a list of instances that are running on Amazon EC2. This is a read-only output parameter.")]
        [ReadOnly(true)]
        [OutputAttribute]
        [EditorAttribute(typeof(DockEditor), typeof(UITypeEditor))]
        [BindTypes(new Type[] { typeof(EC2InstanceDescription) })]
        public List<EC2InstanceDescription> Output
        {
            get
            {
                return ((List<EC2InstanceDescription>)(base.GetValue(OutputProperty)));
            }
            set
            {
                base.SetValue(OutputProperty, value);
            }
        }
        #endregion Output Property

        #endregion Dependency Properties

        // The logic for your activity belongs in the Execute method below
        #region Activity Execution Logic
        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            // Expand all string properties with the required attribute
            ExpandStringProperties(executionContext.Activity);

            try
            {
                //connect to EC2
                AmazonEC2 service = new AmazonEC2Client(this.AccessKey, GlobalUtilities.GetPasswordValue(this.SecretKey, this.HiddenSecretKey));

                //setup the request
                DescribeInstancesRequest request = new DescribeInstancesRequest();
                request.InstanceId = new List<string>();
                
                //start the instance
                DescribeInstancesResponse response = service.DescribeInstances(request);

                List<EC2InstanceDescription> instances = new List<EC2InstanceDescription>();
                EC2InstanceDescription instanceResponse = new EC2InstanceDescription();
                foreach (Reservation reservation in response.DescribeInstancesResult.Reservation)
                {
                    foreach (RunningInstance instance in reservation.RunningInstance)
                    {
                        instanceResponse = new EC2InstanceDescription();
                        instanceResponse.ReservationId = reservation.ReservationId;
                        instanceResponse.AmiLaunchIndex = instance.AmiLaunchIndex;
                        instanceResponse.ImageId = instance.ImageId;
                        instanceResponse.InstanceId = instance.InstanceId;
                        instanceResponse.InstanceState = instance.InstanceState.Name;
                        instanceResponse.InstanceType = instance.InstanceType;
                        instanceResponse.KernelId = instance.KernelId;
                        instanceResponse.KeyName = instance.KeyName;
                        instanceResponse.LaunchTime = instance.LaunchTime;
                        instanceResponse.PrivateDnsName = instance.PrivateDnsName;
                        instanceResponse.PublicDnsName = instance.PublicDnsName;
                        instanceResponse.RamdiskId = instance.RamdiskId;
                        instanceResponse.StateTransitionReason = instance.StateTransitionReason;
                        instances.Add(instanceResponse);
                    }
                }

                //get the results
                this.Output = instances;
            }

            catch (Exception e)
            {
                String errmsg = "An error has occurred. The error is \"" + e.Message.ToString() + "\"";

                if (e.InnerException != null && e.InnerException.ToString().Length > 0)
                    errmsg += "\nAdditional error info: \"" + e.InnerException.ToString() + "\"";

                throw new Exception(errmsg);

                // NOTE: Do not set ActivityExecutionStatus to Faulting if you throw an exception manually.
            }

            // You need to conclude your Execute method by setting the ActivityExecutionStatus.
            // Valid values are:
            //	Canceling
            //	Closed
            //	Compensating
            //	Executing
            //	Faulting
            //	Initialized
            return ActivityExecutionStatus.Closed;
        }
        #endregion Activity Execution Logic
    }

    // Property validation code
    //     In the Validate method you have to create a new instance of the activity class above,
    //     create a new ValidationErrorCollection instance, and then write the code to process the validation.
    //     For each validation error/warning, you need to add the error/warning to the ValidationErrorCollection.
    //     Finally, the Validate method should return the ValidationErrorCollection object.
    #region Validation Logic
    public class GetInstancesValidator : ActivityValidator
    {
        public override ValidationErrorCollection Validate(ValidationManager manager, object obj)
        {
            GetInstances MyActivity = obj as GetInstances;
            ValidationErrorCollection errs = new ValidationErrorCollection();

            if ((MyActivity != null) && (MyActivity.Parent != null))
            {
                // You can either set validation warnings or errors.
                //     To set a warning, pass 'true' for the 3rd parameter to errs.Add.
                //     To set an error, pass 'false' for the 3rd parameter to errs.Add.
                if (!GlobalUtilities.StringValidates(MyActivity, MyActivity.AccessKey, GetInstances.AccessKeyProperty))
                    errs.Add(new ValidationError(@"You must specify a value for the 'Access Key ID' property.", 101, false, @"AccessKey"));

                if (!GlobalUtilities.PasswordValidates(MyActivity, MyActivity.HiddenSecretKey, GetInstances.SecretKeyProperty, null, GetInstances.SecretKeyProperty.Name))
                    errs.Add(new ValidationError(@"You must specify a value for the 'Secret Access Key' property.", 101, false, @"SecretKey"));
            }

            #region StopOnError Validation
            if (!MyActivity.StopOnError)
            {
                Boolean found = false;
                if (MyActivity.Activities != null)
                {
                    for (int i = 0; ((i < MyActivity.Activities.Count) && (!found)); i++)
                    {
                        if (MyActivity.Activities[i].GetType() == typeof(System.Workflow.ComponentModel.FaultHandlersActivity))
                        {
                            found = true;
                            CompositeActivity faultHandlers = (CompositeActivity)(MyActivity.Activities[i]);
                            if ((faultHandlers.Activities == null) || (faultHandlers.Activities.Count == 0))
                                errs.Add(new ValidationError("The 'Stop On Error' property is set to 'false' but the auto-generated faultHandlerActivity has been manually deleted.", 101, false, "StopOnError"));
                        }
                    }
                    if (!found)
                        errs.Add(new ValidationError("The 'Stop On Error' property is set to 'false' but the auto-generated faultHandlersActivity has been manually deleted.", 101, false, "StopOnError"));
                }
            }
            #endregion StopOnError Validation


            // Return the ValidationErrorCollection
            return errs;
        }
    }
    #endregion Validation Logic
}
